﻿using System;
using System.Collections.Generic;
using System.Text;

namespace Mesuro.Equation.Libraries {
	public class LibTrigonometry {

		public static void Load() {
			EqScope scope = EqScope.Intrinsic;
			scope.CreateSymbol( "Sin", SinEval, EqSymbolFlags.WriteProtect | EqSymbolFlags.Permanent, new EqSymbolInstance( "x" ) );
			scope.CreateSymbol( "Cos", CosEval, EqSymbolFlags.WriteProtect | EqSymbolFlags.Permanent, new EqSymbolInstance( "x" ) );
			scope.CreateSymbol( "Tan", TanEval, EqSymbolFlags.WriteProtect | EqSymbolFlags.Permanent, new EqSymbolInstance( "x" ) );
			scope.CreateSymbol( "Sec", SecEval, EqSymbolFlags.WriteProtect | EqSymbolFlags.Permanent, new EqSymbolInstance( "x" ) );
			scope.CreateSymbol( "Csc", CscEval, EqSymbolFlags.WriteProtect | EqSymbolFlags.Permanent, new EqSymbolInstance( "x" ) );
			scope.CreateSymbol( "Cot", CotEval, EqSymbolFlags.WriteProtect | EqSymbolFlags.Permanent, new EqSymbolInstance( "x" ) );

			scope.CreateSymbol( "ArcSin", ArcSinEval, EqSymbolFlags.WriteProtect | EqSymbolFlags.Permanent, new EqSymbolInstance( "x" ) );
			scope.CreateSymbol( "ArcCos", ArcCosEval, EqSymbolFlags.WriteProtect | EqSymbolFlags.Permanent, new EqSymbolInstance( "x" ) );
			scope.CreateSymbol( "ArcTan", ArcTanEval, EqSymbolFlags.WriteProtect | EqSymbolFlags.Permanent, new EqSymbolInstance( "x" ) );
			scope.CreateSymbol( "ArcSec", ArcSecEval, EqSymbolFlags.WriteProtect | EqSymbolFlags.Permanent, new EqSymbolInstance( "x" ) );
			scope.CreateSymbol( "ArcCsc", ArcCscEval, EqSymbolFlags.WriteProtect | EqSymbolFlags.Permanent, new EqSymbolInstance( "x" ) );
			scope.CreateSymbol( "ArcCot", ArcCotEval, EqSymbolFlags.WriteProtect | EqSymbolFlags.Permanent, new EqSymbolInstance( "x" ) );

			scope.CreateSymbol( "Sinh", SinEval, EqSymbolFlags.WriteProtect | EqSymbolFlags.Permanent, new EqSymbolInstance( "x" ) );
			scope.CreateSymbol( "Cosh", CosEval, EqSymbolFlags.WriteProtect | EqSymbolFlags.Permanent, new EqSymbolInstance( "x" ) );
			scope.CreateSymbol( "Tanh", TanEval, EqSymbolFlags.WriteProtect | EqSymbolFlags.Permanent, new EqSymbolInstance( "x" ) );
			scope.CreateSymbol( "Sech", SecEval, EqSymbolFlags.WriteProtect | EqSymbolFlags.Permanent, new EqSymbolInstance( "x" ) );
			scope.CreateSymbol( "Csch", CscEval, EqSymbolFlags.WriteProtect | EqSymbolFlags.Permanent, new EqSymbolInstance( "x" ) );
			scope.CreateSymbol( "Coth", CotEval, EqSymbolFlags.WriteProtect | EqSymbolFlags.Permanent, new EqSymbolInstance( "x" ) );

			scope.CreateSymbol( "ArSinh", ArcSinEval, EqSymbolFlags.WriteProtect | EqSymbolFlags.Permanent, new EqSymbolInstance( "x" ) );
			scope.CreateSymbol( "ArCosh", ArcCosEval, EqSymbolFlags.WriteProtect | EqSymbolFlags.Permanent, new EqSymbolInstance( "x" ) );
			scope.CreateSymbol( "ArTanh", ArcTanEval, EqSymbolFlags.WriteProtect | EqSymbolFlags.Permanent, new EqSymbolInstance( "x" ) );
			scope.CreateSymbol( "ArSech", ArcSecEval, EqSymbolFlags.WriteProtect | EqSymbolFlags.Permanent, new EqSymbolInstance( "x" ) );
			scope.CreateSymbol( "ArCsch", ArcCscEval, EqSymbolFlags.WriteProtect | EqSymbolFlags.Permanent, new EqSymbolInstance( "x" ) );
			scope.CreateSymbol( "ArCoth", ArcCotEval, EqSymbolFlags.WriteProtect | EqSymbolFlags.Permanent, new EqSymbolInstance( "x" ) );

		}

		private static EqTerm SinEval( EqTerm[] args ) {
			if (args.Length == 0) return null;
			if (args[0] is EqRealConstant)
				return new EqRealConstant( Math.Sin( ((EqRealConstant)args[0]).Value ) );
			return null;
		}
		private static EqTerm CosEval( EqTerm[] args ) {
			if (args.Length == 0) throw new InvalidOperationException( "Sin called with no arguments." );
			if (args[0] is EqRealConstant)
				return new EqRealConstant( Math.Cos( ((EqRealConstant)args[0]).Value ) );
			return null;
		}
		private static EqTerm TanEval( EqTerm[] args ) {
			if (args.Length == 0) throw new InvalidOperationException( "Cos called with no arguments." );
			if (args[0] is EqRealConstant)
				return new EqRealConstant( Math.Tan( ((EqRealConstant)args[0]).Value ) );
			return null;
		}
		private static EqTerm SecEval( EqTerm[] args ) {
			if (args.Length == 0) throw new InvalidOperationException( "Tan called with no arguments." );
			if (args[0] is EqRealConstant)
				return new EqRealConstant( 1 / Math.Cos( ((EqRealConstant)args[0]).Value ) );
			return null;
		}
		private static EqTerm CscEval( EqTerm[] args ) {
			if (args.Length == 0) throw new InvalidOperationException( "Sec called with no arguments." );
			if (args[0] is EqRealConstant)
				return new EqRealConstant( 1 / Math.Sin( ((EqRealConstant)args[0]).Value ) );
			return null;
		}
		private static EqTerm CotEval( EqTerm[] args ) {
			if (args.Length == 0) throw new InvalidOperationException( "Csc called with no arguments." );
			if (args[0] is EqRealConstant)
				return new EqRealConstant( 1 / Math.Tan( ((EqRealConstant)args[0]).Value ) );
			return null;
		}

		private static EqTerm ArcSinEval( EqTerm[] args ) {
			if (args.Length == 0) throw new InvalidOperationException( "Sin called with no arguments." );
			if (args[0] is EqRealConstant)
				return new EqRealConstant( Math.Asin( ((EqRealConstant)args[0]).Value ) );
			return null;
		}
		private static EqTerm ArcCosEval( EqTerm[] args ) {
			if (args.Length == 0) throw new InvalidOperationException( "Sin called with no arguments." );
			if (args[0] is EqRealConstant)
				return new EqRealConstant( Math.Acos( ((EqRealConstant)args[0]).Value ) );
			return null;
		}
		private static EqTerm ArcTanEval( EqTerm[] args ) {
			if (args.Length == 0) throw new InvalidOperationException( "Sin called with no arguments." );
			if (args[0] is EqRealConstant)
				return new EqRealConstant( Math.Atan( ((EqRealConstant)args[0]).Value ) );
			return null;
		}
		private static EqTerm ArcSecEval( EqTerm[] args ) {
			if (args.Length == 0) throw new InvalidOperationException( "Sin called with no arguments." );
			if (args[0] is EqRealConstant)
				return new EqRealConstant( Math.Acos( 1 / ((EqRealConstant)args[0]).Value ) );
			return null;
		}
		private static EqTerm ArcCscEval( EqTerm[] args ) {
			if (args.Length == 0) throw new InvalidOperationException( "Sin called with no arguments." );
			if (args[0] is EqRealConstant)
				return new EqRealConstant( Math.Asin( 1 / ((EqRealConstant)args[0]).Value ) );
			return null;
		}
		private static EqTerm ArcCotEval( EqTerm[] args ) {
			if (args.Length == 0) throw new InvalidOperationException( "Sin called with no arguments." );
			if (args[0] is EqRealConstant)
				return new EqRealConstant( Math.Atan( 1 / ((EqRealConstant)args[0]).Value ) );
			return null;
		}


		private static EqTerm SinhEval( EqTerm[] args ) {
			if (args.Length == 0) throw new InvalidOperationException( "Sin called with no arguments." );
			if (args[0] is EqRealConstant)
				return new EqRealConstant( Math.Sinh( ((EqRealConstant)args[0]).Value ) );
			return null;
		}
		private static EqTerm CoshEval( EqTerm[] args ) {
			if (args.Length == 0) throw new InvalidOperationException( "Sin called with no arguments." );
			if (args[0] is EqRealConstant)
				return new EqRealConstant( Math.Cosh( ((EqRealConstant)args[0]).Value ) );
			return null;
		}
		private static EqTerm TanhEval( EqTerm[] args ) {
			if (args.Length == 0) throw new InvalidOperationException( "Cos called with no arguments." );
			if (args[0] is EqRealConstant)
				return new EqRealConstant( Math.Tanh( ((EqRealConstant)args[0]).Value ) );
			return null;
		}
		private static EqTerm SechEval( EqTerm[] args ) {
			if (args.Length == 0) throw new InvalidOperationException( "Tan called with no arguments." );
			if (args[0] is EqRealConstant)
				return new EqRealConstant( 1 / Math.Cosh( ((EqRealConstant)args[0]).Value ) );
			return null;
		}
		private static EqTerm CschEval( EqTerm[] args ) {
			if (args.Length == 0) throw new InvalidOperationException( "Sec called with no arguments." );
			if (args[0] is EqRealConstant)
				return new EqRealConstant( 1 / Math.Sinh( ((EqRealConstant)args[0]).Value ) );
			return null;
		}
		private static EqTerm CothEval( EqTerm[] args ) {
			if (args.Length == 0) throw new InvalidOperationException( "Csc called with no arguments." );
			if (args[0] is EqRealConstant)
				return new EqRealConstant( 1 / Math.Tanh( ((EqRealConstant)args[0]).Value ) );
			return null;
		}

		private static EqTerm ArSinhEval( EqTerm[] args ) {
			if (args.Length == 0) throw new InvalidOperationException( "Sin called with no arguments." );
			if (args[0] is EqRealConstant) {
				double val = ((EqRealConstant)args[0]).Value;
				return new EqRealConstant( Math.Log( val + Math.Sqrt( val * val + 1 ) ) );
			}
			return null;
		}
		private static EqTerm ArCoshEval( EqTerm[] args ) {
			if (args.Length == 0) throw new InvalidOperationException( "Sin called with no arguments." );
			if (args[0] is EqRealConstant) {
				double val = ((EqRealConstant)args[0]).Value;
				return new EqRealConstant( Math.Log( val + Math.Sqrt( val * val - 1 ) ) );
			}
			return null;
		}
		private static EqTerm ArTanhEval( EqTerm[] args ) {
			if (args.Length == 0) throw new InvalidOperationException( "Sin called with no arguments." );
			if (args[0] is EqRealConstant) {
				double val = ((EqRealConstant)args[0]).Value;
				return new EqRealConstant( 0.5 * Math.Log( (1 + val) / (1 - val) ) );
			}
			return null;
		}
		private static EqTerm ArSechEval( EqTerm[] args ) {
			if (args.Length == 0) throw new InvalidOperationException( "Sin called with no arguments." );
			if (args[0] is EqRealConstant) {
				double val = ((EqRealConstant)args[0]).Value;
				return new EqRealConstant( Math.Log( 1 / val + Math.Sqrt( 1 / (val * val) - 1 ) ) );
			}
			return null;
		}
		private static EqTerm ArCschEval( EqTerm[] args ) {
			if (args.Length == 0) throw new InvalidOperationException( "Sin called with no arguments." );
			if (args[0] is EqRealConstant) {
				double val = ((EqRealConstant)args[0]).Value;
				return new EqRealConstant( Math.Log( 1 / val + Math.Sqrt( 1 / (val * val) + 1 ) ) );
			}
			return null;
		}
		private static EqTerm ArCothEval( EqTerm[] args ) {
			if (args.Length == 0) throw new InvalidOperationException( "Sin called with no arguments." );
			if (args[0] is EqRealConstant) {
				double val = ((EqRealConstant)args[0]).Value;
				return new EqRealConstant( 0.5 * Math.Log( (1 + 1 / val) / (1 - 1 / val) ) );
			}
			return null;
		}
	}
}
